/*CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
 C      io_device.c
 C      Application Name: tcss422-project2
 C      Programmers Names: Evan Phillips, Lucas Cowell, Tony Butterfield
 C      Date created:   May 13, 2011
 C      Date Completed: 
 C      Brief Description: simulated generic io device.  When it recieves an io request, it will
 C                  return an io interrupt after a random interval.
 C
 CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC*/
#include <stdlib.h>
#include <stddef.h>
#include <time.h>
#include "definitions.h"
#include "cpu_sim.h"
#include "process_queue.h"
#include "io_device.h"

/*=================================================================================================
 =  Comment block for function: iodev_init()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      IODevice iodev_init(CPU)
 =      Return value ranges: pointer to IODevice
 =      Parameter value ranges: IO device interrupt, pointer to CPU, pointer to error return.
 =      Error conditions: NULL_POINTER_ERROR
 =  Initializes an IODevice simulation.
 =
 ==================================================================================================*/
IODevice iodev_init(int interrupt, CPU cpu, int* error)
{
    if (cpu == NULL) *error |= NULL_POINTER_ERROR;

    IODevice d = (IODevice) malloc(sizeof(IODeviceStr));
    d->blocked_q = p_queue_init();
    d->io_interrupt = interrupt;
    d->clock_counter = 0;
    d->working = 0;
    d->cpu = cpu;

    return d;
}

/*=================================================================================================
 =  Comment block for function: iodev_free()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      int iodev_free(IODevice)
 =      Return value ranges: 0 on success, else error code
 =      Parameter value ranges: pointer to IODevice
 =      Error conditions: NULL_POINTER_ERROR
 =  deallocates an IODevice.  (I don't think we really need to implement this one...)
 =
 ==================================================================================================*/
int iodev_free(IODevice d)
{
    if (d == NULL) return NULL_POINTER_ERROR;
    int error = p_queue_free(d->blocked_q);
    free(d);

    return error;
}

/*=================================================================================================
 =  Comment block for function: iodev_tick()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      int iodev_tick(IODevice)
 =      Return value ranges: 0 on success, else error code
 =      Parameter value ranges: pointer to IODevice
 =      Error conditions: NULL_POINTER_ERROR
 =  advances the clock counter simulating work being done.
 =
 ==================================================================================================*/
int iodev_tick(IODevice d)
{
    if (d == NULL) return NULL_POINTER_ERROR;
    int error = 0;
    //d->clock_counter = d->clock_counter + 1;
    if (++d->clock_counter == d->working)   // always increments before checking, so if working == 0
    {                                       // the IO_device is basically "sleeping"
        error = iodev_throwInterrupt(d);
    }

    d->clock_counter = d->clock_counter % (int) MAX_IO_TIME_DELAY;  // protect from integer overflow

    return error;
}

/*=================================================================================================
 =  Comment block for function: iodev_setRequest()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      int iodev_setRequest(IODevice)
 =      Return value ranges: 0 on success, else error code
 =      Parameter value ranges: pointer to IODevice
 =      Error conditions: NULL_POINTER_ERROR
 =  generates a random number of ticks (1-100) to simulate work being done and initializes the
 =  clock counter
 =
 ==================================================================================================*/
int iodev_setRequest(IODevice d)
{
    if (d == NULL) return NULL_POINTER_ERROR;

    /* Simple "srand()" seed: just use "time()" */
    unsigned int iseed = (unsigned int)time(NULL);
    srand(iseed);

    d->working = 1 + (int)(MAX_IO_TIME_DELAY * rand() / (RAND_MAX + 1.0));
    d->clock_counter = 0;

    return 0;
}

/*=================================================================================================
 =  Comment block for function: iodev_throwInterrupt()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      int iodev_throwInterrupt(IODevice)
 =      Return value ranges: 0 on success, else error code
 =      Parameter value ranges: pointer to IODevice
 =      Error conditions: NULL_POINTER_ERROR
 =  sends an IO_INTERRUPT to the cpu and resets the working interval to 0 putting the device to
 =  "sleep"
 =
 ==================================================================================================*/
int iodev_throwInterrupt(IODevice d)
{
    if (d == NULL) return NULL_POINTER_ERROR;

    d->cpu->interrupt_controller |= d->io_interrupt;    // use |= to sim more than one interrupt.
    d->working = 0;
    return 0;
}
